
#include <assert.h>

#include "log_api.h"
#include "OSThread.h"
#ifndef WIN32
#include <unistd.h>
#endif

#ifdef WIN32
DWORD   OSThread::sThreadStorageIndex = 0;
#elif __PTHREADS__
pthread_key_t OSThread::gMainKey = 0;
#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
//pthread_attr_t OSThread::sThreadAttr;
#endif
#endif

OSThread::OSThread():fStopRequested(false),
    fJoined(false)
{
}


OSThread::~OSThread()
{
}

void OSThread::Start()
{
#ifdef WIN32
    unsigned int theId = 0; // We don't care about the identifier
    fThreadID = (HANDLE)_beginthreadex(NULL,   // Inherit security
        0,      // Inherit stack size
        _Entry, // Entry function
        (void*)this,    // Entry arg
        0,      // Begin executing immediately
        &theId);
    assert(fThreadID != NULL);
    //SetThreadPriority(fThreadID, THREAD_PRIORITY_LOWEST);
#elif __PTHREADS__
    pthread_attr_t* theAttrP;
#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
    //theAttrP = &sThreadAttr;
    theAttrP = 0;
#else
    theAttrP = NULL;
#endif
    int err = pthread_create((pthread_t*)&fThreadID, theAttrP, _Entry, (void*)this);
    assert(err == 0);
#else
    fThreadID = (UInt32)cthread_fork((cthread_fn_t)_Entry, (any_t)this);
#endif
}

void OSThread::Sleep(UInt32 milliseconds)
{
#ifdef __Win32__
    ::Sleep(milliseconds);
#else
    usleep(milliseconds * 1000);
#endif
}

#ifdef __Win32__
unsigned int WINAPI OSThread::_Entry(LPVOID inThread)
#else
void* OSThread::_Entry(void *inThread)  //static
#endif
{
    OSThread* theThread = (OSThread*)inThread;
#ifdef __Win32__
    BOOL theErr = ::TlsSetValue(sThreadStorageIndex, theThread);
    assert(theErr == TRUE);
#elif __PTHREADS__
    theThread->fThreadID = (pthread_t)pthread_self();
    pthread_setspecific(OSThread::gMainKey, theThread);
#else
    theThread->fThreadID = (UInt32)cthread_self();
    cthread_set_data(cthread_self(), (any_t)theThread);
#endif
    theThread->SwitchPersonality();
    //
    // Run the thread
    theThread->Entry();
    return NULL;
}

Bool16  OSThread::SwitchPersonality()
{
//#if __linux__
//    if (::strlen(sGroup) > 0)
//    {
//        struct group* gr = ::getgrnam(sGroup);
//        if (gr == NULL || ::setgid(gr->gr_gid) == -1)
//        {
//            //qtss_printf("thread %lu setgid  to group=%s FAILED \n", (UInt32) this, sGroup);
//            return false;
//        }
//
//        //qtss_printf("thread %lu setgid  to group=%s \n", (UInt32) this, sGroup);
//    }
//
//
//    if (::strlen(sUser) > 0)
//    {
//        struct passwd* pw = ::getpwnam(sUser);
//        if (pw == NULL || ::setuid(pw->pw_uid) == -1)
//        {
//            //qtss_printf("thread %lu setuid  to user=%s FAILED \n", (UInt32) this, sUser);
//            return false;
//        }
//
//        //qtss_printf("thread %lu setuid  to user=%s \n", (UInt32) this, sUser);
//    }
//#endif

    return true;
}


#ifdef __Win32__
int OSThread::GetErrno()
{
    int winErr = ::GetLastError();


    // Convert to a POSIX errorcode. The *major* assumption is that
    // the meaning of these codes is 1-1 and each Winsock, etc, etc
    // function is equivalent in errors to the POSIX standard. This is 
    // a big assumption, but the server only checks for a small subset of
    // the real errors, on only a small number of functions, so this is probably ok.
	switch (winErr)
	{
		case ERROR_FILE_NOT_FOUND: return ENOENT;
		case ERROR_PATH_NOT_FOUND: return ENOENT;

		case WSAEINTR:      return EINTR;
		case WSAENETRESET:  return EPIPE;
		case WSAENOTCONN:   return ENOTCONN;
		case WSAEWOULDBLOCK:return EAGAIN;
		case WSAECONNRESET: return EPIPE;
		case WSAEADDRINUSE: return EADDRINUSE;
		case WSAEMFILE:     return EMFILE;
		case WSAEINPROGRESS:return EINPROGRESS;
		case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
		case WSAECONNABORTED: return EPIPE;
		case 0:             return 0;

		default:
		{
			LOG_OUTPUT("winErr=%d\r\n", winErr);
			return ENOTCONN;
		}
	}
}
#endif
void OSThread::StopAndWaitForThread()
{
    fStopRequested = true;
    if (!fJoined)
        Join();
}

void OSThread::Join()
{
    // What we're trying to do is allow the thread we want to delete to complete
    // running. So we wait for it to stop.
    assert(!fJoined);
    fJoined = true;
#ifdef __Win32__
    DWORD theErr = ::WaitForSingleObject(fThreadID, INFINITE);
    //assert(theErr == WAIT_OBJECT_0);
#elif __PTHREADS__
    void *retVal;
    pthread_join((pthread_t)fThreadID, &retVal);
#else
    cthread_join((cthread_t)fThreadID);
#endif
}

